
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.Threading;
using System.Xml;


using Swaf;
using Swaf.Call;
using Swaf.BizObj;
using Swaf.BizRule;
using Swaf.Config;
using Swaf.Instrumentation;
using Swaf.Logging;
using Swaf.Resource;
using Swaf.Container;
using Swaf.Gui;
using Swaf.Security;

namespace Swaf
{
	/// <summary>
	/// The pure C# full implementation of the <c>IApplication</c> and 
	/// <c>IAppManagement</c> interfaces of SWAF.</summary>
	/// <remarks>
	/// This class fully implements the interface and is the only implementation
	/// needed in a single user client application.  When being used in a multi-user
	/// environment such as <i>ASP</i> or <i>ASP.NET</i> this class is the application
	/// instance.  Individual "user sessions" would use a <c>SessionApp</c> instance
	/// in order to gain user specific <c>sessionData</c>.</remarks>
	public class Application : MarshalByRefObject, IApplication, IAppManagement, IConfigPartner
	{
		public const int NORMAL_STARTUP_LOG_LEVEL = 99999;
		public const int VERBOSE_STARTUP_LOG_LEVEL=99998;
		public const int DEFAULT_FRAMEWORK_LOG_LEVEL = -1;
		private IAudit m_audit;
		private ITrace m_trace;
		private ILog m_log;
		private BizObjManager m_bizObjMgr;
		private RuleManager m_ruleMgr;
		private ResourceManager m_resMgr;
		private IGuiManager m_guiMgr;
		private SecurityManager m_securityMgr=null;
		private ICall m_sessionValidator;
		private IMap m_sessionData;
		private IMap m_appData;
		private Hashtable m_factories;
		private IFactoryManager m_factoryMgr;
		private string m_sessionId=Guid.NewGuid().ToString();
		private IApplicationChild m_childApp;
		private static IList s_startupAppDataEntries;
		private string m_sessionLock="LockMe";
		private CultureInfo m_currentCulture;

		private  int m_loggingThreshold=0; //This variable is used to hold the configured threshold during startup.
		/// <summary>
		/// Current implementation uses a static variable to house the domain specific
		/// Applicataion object.  Clients call the static create method to get that
		/// instance created.  A rewrite of this might store an instance of Application in
		/// the .NET AppDomain.CurrentDomain() storage to segment application objects
		/// by domain rather then C# static as done now.
		/// </summary>
		private static IApplication s_domainApp = null;
		private static Application s_mainApp = null;
		private static LocalDataStoreSlot s_sessionAppThreadSlot = Thread.GetNamedDataSlot("SwafSessionApp");

		private  bool m_isLoading = true;
		private  bool m_browseMode = false;
		private  int  m_startupLoggingLevel=VERBOSE_STARTUP_LOG_LEVEL;
		private  string m_frameworkVersion="";
		private IPerformanceCounter m_sessionCounter;
		private bool m_sessioncounterUnitialized=true;

		// Event declaration for Application and Session Start/Shutdown events
		private IList m_appShutdownEvents = new ArrayList();
		public event AppShutdownRequest OnAppShutdown
		{
			add
			{
				m_appShutdownEvents.Add(value);
			}
			remove
			{
				m_appShutdownEvents.Remove(value);
			}
		}

		public event AppStartRequest OnAppStart;
		public event SessionShutdownRequest OnSessionShutdown;
		public event SessionStartRequest OnSessionStart;

		protected void FireAppShutdownEvent(object dummy)
		{
			AutoResetEvent shutdownComplete = dummy as AutoResetEvent;
			
			for (int i = (m_appShutdownEvents.Count - 1); i >= 0; --i)
			{
				try
				{
					((AppShutdownRequest)m_appShutdownEvents[i])(this);
				}
				catch (Exception e)
				{
					Log.println("Unable to execute shutdown event, {0}", e.ToString());
				}
			}

			if (shutdownComplete != null)
			{
				shutdownComplete.Set();
			}
		}

		/// <summary>
		/// Returns the current version number as major.minor.build.revision of the framework as defined in AssemblySharedInfo.cs
		/// </summary>
		public static string FrameworkVersion
		{
			get{return s_mainApp.m_frameworkVersion;}
		}
		/// <summary>
		/// Returns a boolean indicating whether this instance of the Application is currently loading configurations.
		/// </summary>
		public static bool IsLoading
		{
			get
			{
				return s_mainApp.m_isLoading;
			}
		}

		public bool inBrowseMode
		{
			get{return m_browseMode;}
		}

		public IResults executeRule(string sessionId, bool isTiedToGlobalApp, string userName, INameAddressSupport additionalInfo, string instanceId, string currentCulture,
			string methodName, ICollection ruleParams, bool multiCall)
		{
			throw new NotSupportedException("Method is for use for remote application processing alone.");
		}

        public IResults executeRule(string sessionId, bool isTiedToGlobalApp, string userName, INameAddressSupport additionalInfo, string instanceId, string currentCulture,
			string methodName, object ruleParams)
		{
			throw new NotSupportedException("Method is for use for remote application processing alone.");
		}


	public static IApplication create(string configXml, string configParam1, string configParam2)
		{
			return create(configXml,configParam1,configParam2,null);
		}

		public static IApplication create(string configXml, string configParam1, string configParam2, IApplicationChild appPartner)
		{
			return create(configXml, configParam1, configParam2, appPartner, false);
		}

		private static IApplication create(string configXml, string configParam1, string configParam2, IApplicationChild appPartner, bool browseMode)
		{
			Application app=null;
			s_startupAppDataEntries = new ArrayList();
			try
			{
				app = new Application();
				app.m_browseMode = browseMode;
				if (appPartner != null)
				{
					app.m_childApp = appPartner;
					s_domainApp = appPartner;
				}
				else
				{
					s_domainApp=app;
				}
				BaseException.defineLog(app.Log);
				app.config(configXml, configParam1, configParam2);
				BaseException.defineLog(app.Log);
				BaseException.defineAudit(app.Audit);
				app.m_isLoading=false;


				return app;
			}
			catch(Exception e)
			{
				//Shutdown the created app, then rethrow the error.
				try
				{
					((IAppManagement) app).shutdown();
				}
				catch(Exception){}
				throw;
			}
		}

		public static IApplication createInBrowseMode(string configXml, string configParam1, string configParam2)
		{
			return create(configXml, configParam1, configParam2, null, true);
		}

		public static IApplication currentApp
		{get{
			IApplication sessionApp = (IApplication)Thread.GetData(s_sessionAppThreadSlot);
			return (sessionApp == null)?s_domainApp:sessionApp;
			 //return s_domainApp;
		}
			set
			{
				Thread.SetData(s_sessionAppThreadSlot, value);
			}
		}

		public static IApplication globalApp
		{get{
				return s_domainApp;
		}}

		public void shutdown()
		{
			try
			{
				if(!inBrowseMode)
				{
					AutoResetEvent completeEvent = new AutoResetEvent(false);
					ThreadPool.QueueUserWorkItem(new WaitCallback(FireAppShutdownEvent), completeEvent);

					int waitLength = 3 * 60 * 1000; // default to 3 minutes

					string waitLengthText = resMgr.resolve("$CSFMaxShutdownMilliseconds$");
					if (waitLengthText != null && waitLengthText.Length != 0 && waitLengthText[0] != '$')
					{
						try
						{
							waitLength = Convert.ToInt32(waitLengthText);
						}
						catch (Exception)
						{
							// Log that using default value of 3 minutes
							Log.println("Unable to convert $CSFMaxShutdownMillisecons$ macro to an int, {0}.  Using default value of {1}", waitLengthText, waitLength);
						}
					}
					
					bool wasSignaled = completeEvent.WaitOne(waitLength, false);
					if (! wasSignaled)
					{
						Log.println("Complete shutdown did not complete before max shutdown time was exceeded.");
					}
				}
			}
			catch(Exception e)
			{
				//Log the event and intentially eat the exception
				Log.println("Exception attempting to perform application shutdown: {0}", e.ToString());
            }
            finally
            {
                s_domainApp = null;
                s_mainApp = null;
			}
		}


		private Application()
		{
			s_mainApp = this;
			//TODO: Replace hard coded creates of logging stuff to factory.
			m_audit = new Swaf.Logging.Audit("AuditLogs");
			m_trace = new Swaf.Logging.Trace("TraceLogs", 0,0);
			m_log = new Swaf.Logging.Log("MainLogs", 0,0);
			//Set the notify threshold to the startup normal until the logging config is read.
			m_log.NotifyThreshold=VERBOSE_STARTUP_LOG_LEVEL;
			
			m_sessionData = new FlexiMap();
			m_appData = new FlexiMap();
			m_factories = new Hashtable();
			m_factoryMgr = new FactoryManager(m_factories);
			m_securityMgr = new SecurityManager();
			m_currentCulture=CultureInfo.CurrentCulture;
			m_bizObjMgr = new BizObjManager(this);
			m_ruleMgr = new RuleManager(this);
			m_resMgr = new ResourceManager(this);

			OnAppShutdown += new AppShutdownRequest(m_ruleMgr.stop);
			OnAppShutdown += new AppShutdownRequest(ThreadedCallbackQueue.shutdownThreadGroups);
			Version versionInfo = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
			m_frameworkVersion = string.Format("{0}.{1}.{2}.{3}",versionInfo.Major,versionInfo.Minor, versionInfo.Build, versionInfo.Revision);
			this.Log.println(NORMAL_STARTUP_LOG_LEVEL, "");
			this.Log.println(NORMAL_STARTUP_LOG_LEVEL, "*******************************************************************");
			this.Log.println(NORMAL_STARTUP_LOG_LEVEL,"Starting Swaf Framework Version {0}", Application.FrameworkVersion );
			this.Log.println(NORMAL_STARTUP_LOG_LEVEL, "*******************************************************************");
		}

		protected void config(string configXml, string configParam1, string configParam2)
		{
			//TODO: For now do nothing about configParam1 and configParam2.
			//These are for ResourceManager to use later.
			Log.println(NORMAL_STARTUP_LOG_LEVEL, "***********  Starting new Swaf application instance ***********");
			ConfigManager cm = new ConfigManager(this);
			if(m_childApp != null)
				m_childApp.setupForConfig(this, cm);

			//Setup must have configuration handlers...
			cm.registerConfigPartner(this);
			cm.registerConfigPartner(m_bizObjMgr);
			cm.registerConfigPartner(m_ruleMgr);
			cm.registerConfigPartner(m_resMgr);
			cm.registerConfigPartner(m_securityMgr);

			m_resMgr.defineMacro("ConfigParam1", configParam1);
			m_resMgr.defineMacro("ConfigParam2", configParam2);

			cm.readConfigFile(configXml);
			//Set the NotifyThreshold back to the configured value after startup is complete!
			Log.println(NORMAL_STARTUP_LOG_LEVEL , "***********  Swaf application initialization complete.  ***********");
			Log.NotifyThreshold = m_loggingThreshold;
			foreach(LogCall call in Log.Observers)
			{
				call.startupComplete();
			}

			foreach(AppDataEntry entry in  s_startupAppDataEntries)
			{
				try
				{
					entry.register();
				}
				catch(Exception e)
				{
					throw;
				}
			}
			s_startupAppDataEntries.Clear();
			s_startupAppDataEntries=null;

			m_appData = (IMap) factories[@"Dictionaries"].createFromNickName(@"appdata", m_appData);
			m_sessionData = (IMap) factories[@"Dictionaries"].createFromNickName(@"sessiondata", m_sessionData);

			try
			{
				if(!inBrowseMode && OnAppStart != null)
					OnAppStart(this);
			}
			catch(Exception e)
			{
                // Need to throw exceptoin 
                throw;
			}
			if (m_sessionCounter == null && m_sessioncounterUnitialized)
			{
				IAppInstrumentation instMgr = Application.currentApp.appData["sysObjs-AppInstrumentation"] as IAppInstrumentation;
				if (instMgr != null)
					m_sessionCounter=instMgr.registerCounterHandler("SessionApplications","counter");
				m_sessioncounterUnitialized=false;
			}

		}

		public void checkConfigStatus()
		{
			// TODO:  Add Application.checkConfigStatus implementation
		}
		protected void defineAppDateEntry(AppDataEntry entry)
		{
			
		}
		public void registerForConfig(IConfigManager cm)
		{
			//All static class factory references should be reset in order to propery create again after a shutdown.
			//Most take care of themselves via their package's config partner, but these dont have one so this does it
			ContainerBase.resetClassFactoryReferences();

			cm.registerHandler("...<ClassFactory>", new FactoryConfigHandler(this));
			cm.registerHandler("...<ClassFactory> <Class>", new FactoryItemConfigHandler());
			cm.registerHandler("...<ClassFactory> <Class> <Pattern>", new FactoryItemPatternConfigHandler());
			cm.registerHandler("...<ClassFactory> <Class> <Default>", new FactoryItemDefaultConfigHandler());
			cm.registerHandler("...<ClassFactory> <Class> <Property>", new FactoryItemCustomPropConfigHandler());
			
			cm.registerHandler("...<ApplicationData> <Element>", new AppSesDataElementConfigHandler(this.m_appData, s_startupAppDataEntries));
			cm.registerHandler("...<ApplicationData> <Entry>", new AppSesDataElementConfigHandler(this.m_appData,s_startupAppDataEntries));
			cm.registerHandler("...<LogObserver>", new LogObserversConfigHandler());
			cm.registerHandler("...<LoggingLevels>", new LoggingLevelsConfigHandler());
			cm.registerHandler("...<Timing>", new TimingConfigHandler());
			cm.registerHandler("...<ExceptionLogging>", new ExceptionLoggingConfigHandler());
			cm.registerHandler("...<RemotingSupport>", new RemotingSupportConfigHandler());
			cm.registerHandler("...<ApplicationEvents>", new ApplicationEventsConfigHandler(this));
			cm.registerHandler("...<SessionValidator>", new SessionValidatorConfigHandler(this));
			cm.registerHandler("...<Dependencies> <Dependency>", new FeatureDependancyConfigHandler());
		}


		public virtual IAudit Audit {get{return m_audit;}}
		public virtual ILog Log{get{return m_log;}}
		public virtual ITrace Trace{get{return m_trace;}}

		public virtual IBizObjManager bizObjMgr
		{get{
			 return m_bizObjMgr;
		}}

		public virtual IRuleManager ruleMgr
		{get{
			 return m_ruleMgr;
		}}

		public virtual IResourceManager resMgr
		{get{		 
			 return m_resMgr;
		 }}

		public virtual IGuiManager guiMgr
		{
			get
			{
				if(m_guiMgr == null)
				{
					object gm = m_appData["sysObjs-GuiMgr"];
					Debug.Assert(gm != null);
					Debug.Assert(gm is IGuiManager);
					m_guiMgr = (IGuiManager)gm;
				}
				return m_guiMgr;
			}
		}
		public virtual ISecurityManager securityMgr
		{
			get
			{
				return m_securityMgr;
			}
		}

		public virtual IMap appData
		{get{
			 return m_appData;
		 }}

		public virtual IMap sessionData
		{get{
			 return m_sessionData;
		 }}

		public virtual string sessionId
		{get{
			return m_sessionId;
		}}
			public IDictionary factory{get{return new ReadOnlyDictionary(m_factories);}}
		public IFactoryManager factories{get{return m_factoryMgr;}}

		private IDictionary m_sessionApps;
		/// <summary>
		/// Gets/Sets the currently configured culture for this application instance
		/// </summary>
		public virtual CultureInfo culture
		{
			get{return m_currentCulture;}
			set{m_currentCulture=value;}
		}
		/// <summary>
		/// Sets the current culture for this application instance by name.
		/// </summary>
		/// <param name="name"></param>
		public virtual void setCurrentCulture(string name)
		{
			culture=CultureInfo.CreateSpecificCulture(name);
		}

		public virtual IApplication createSessionApplication(string sessionKey)
		{
			return createSessionApplication(sessionKey, false, null, null);
		}
        public virtual IApplication createSessionApplication(string sessionKey, bool isTiedToGlobalApp)
        {
            return createSessionApplication(sessionKey, isTiedToGlobalApp, null, null);
        }
		public virtual IApplication createSessionApplication(string sessionKey, bool isTiedToGlobalApp, IDictionary startingSessionValues)
		{
			IApplication sessionApp=null;
			string sessionExpire="";
			sessionExpire = resMgr.resolve("$SessionExpire$");
			if (sessionExpire.Equals("$SessionExpire$"))
			{
				sessionExpire=null;
			}

            if (m_sessionApps == null)
			    lock(m_sessionLock)
    				if(m_sessionApps == null)
    					m_sessionApps = new FlexiMap();
	
            sessionApp = (IApplication) m_sessionApps[sessionKey];
            if (sessionApp == null)
            {
                sessionApp = new SessionApp(this, sessionKey, startingSessionValues);
                //Establish the default principal as the base principal for this session.
                sessionApp.securityMgr.currentPrincipal = this.securityMgr.currentPrincipal;
                if (!Cmn.IsEmpty(sessionExpire))
                {
                    ((INameAddressSupport)m_sessionApps).put(sessionKey, sessionApp, sessionExpire);
                }
                else
                {
                    //If no SessionExpire macro was found default to pre 1.5.1 behavior, no cache.
                    m_sessionApps[sessionKey] = sessionApp;
                }

                sessionApp.isTiedToRemoteGlobalApp = isTiedToGlobalApp;

                try
                {
                    if (OnSessionStart != null)
                        OnSessionStart(sessionApp);
                }
                catch (Exception e)
                {

                }
                try
                {
                    if (m_sessionCounter != null)
                    {
                        m_sessionCounter.increment();
                    }
                }
                catch (Exception) { }
            }
			return sessionApp;
		}
		public IApplication createSessionApplication(string sessionKey, ISecurityPrincipal currentPrincipal)
		{
			return createSessionApplication(sessionKey, false, currentPrincipal);
		}

        public IApplication createSessionApplication(string sessionKey, bool isTiedToGlobalApp, ISecurityPrincipal currentPrincipal)
        {
            return createSessionApplication(sessionKey, isTiedToGlobalApp, currentPrincipal, null);
        }

		public IApplication createSessionApplication(string sessionKey,bool isTiedToGlobalApp, ISecurityPrincipal currentPrincipal, IDictionary sessionStartValues)
		{
            IApplication sessionApp = createSessionApplication(sessionKey, isTiedToGlobalApp, sessionStartValues);
            if(currentPrincipal != null)
			    sessionApp.securityMgr.currentPrincipal=currentPrincipal;
			return sessionApp;
		}

		public void appSessionComplete(string sessionKey)
		{
			Debug.Assert(m_sessionApps != null);
			
			try
			{
                if(OnSessionShutdown != null)
				    OnSessionShutdown((IApplication)m_sessionApps[sessionKey]);
			}
			catch(Exception e)
			{
			}
			try
			{
				if (m_sessionCounter != null)
				{
					m_sessionCounter.decrement();
				}
			}
			catch(Exception){}

			m_sessionApps.Remove(sessionKey);
		}

		public IApplication getExistingSession(string sessionKey)
		{
			if(m_sessionApps != null)
				return (IApplication)m_sessionApps[sessionKey];
			return null;
		}

		public IDictionary Sessions {get{return m_sessionApps == null?null:new ReadOnlyDictionary(m_sessionApps);}}

		protected internal void registerFactory(string id, IClassFactory factory)
		{
			m_factories[id] = factory;
		}

		protected internal ClassFactory getFactory(string id)
		{
			ClassFactory f = null;
			if(m_factories.ContainsKey(id))
				f = ((ClassFactory.CreateOnlyFactoryImpl)m_factories[id]).m_factory;

			if(f == null)
			{
				f = new ClassFactory(id);
				registerFactory(id, f.CreateOnlyFactory);
			}
			return f;
		}


		/// <summary>
		/// Handles ClassFactory defintions in the application configuration.
		/// </summary>
		protected internal class FactoryConfigHandler : ConfigHandler
		{
			protected Application m_app;
			protected static string[] s_validAttributes = new string[]{"id"};

			public FactoryConfigHandler(Application app)
			{
				m_app = app;
			}
			/// <summary>
			/// Registers the new factory within the appData map and gets the parsing ready to 
			/// add entries to the factory.
			/// </summary>
			/// <param name="context"></param>
			/// <param name="configInfo"></param>
			public override void executeBefore(IConfigElement configInfo)
			{
				IApplication app = Application.currentApp;
				configInfo.checkIllegalAttributes(s_validAttributes);
				string id = configInfo.getAttribute("id");
				Application.currentApp.Log.println(Application.NORMAL_STARTUP_LOG_LEVEL,"Defined Classfactory {0}",id);
				ClassFactory f = m_app.getFactory(id);
				configInfo.Context["factory"] = f;
			}

			public override void  executeAfter(IConfigElement configInfo)
			{
				configInfo.Context.Remove("factory");
			}
		}

		/// <summary>
		/// Handles Class definitions in a specific factory definition
		/// </summary>
		protected internal class FactoryItemConfigHandler : ConfigHandler
		{
			protected static string[] s_validAttributes = new string[]{"type","name","mutable","parsable","factoryMethod", "isBasic"};

			public override void executeBefore(IConfigElement configInfo)
			{
				IApplication app = Application.currentApp;
				configInfo.checkIllegalAttributes(s_validAttributes);
				string type = configInfo.getAttribute("type");
				string name = configInfo.getAttribute("name");
				bool isBasic = Cmn.IsTrue(configInfo.getAttribute("isBasic"));
				string ctorParam = null;//configInfo.getChildText();
				if(ctorParam != null)
					ctorParam = ctorParam.Trim();
				bool mutable = String.Compare(configInfo.getAttribute("mutable", "True"), "true", true) == 0?true:false;
				bool parsable = String.Compare(configInfo.getAttribute("parsable", "False"), "true", true) == 0?true:false;
				string factoryMethod = configInfo.getAttribute("factoryMethod",null);
				if(factoryMethod == null && parsable == true)
					factoryMethod = "Parse";


				DotNetClassInfo i = ((ClassFactory)configInfo.Context["factory"]).add(type, name, ctorParam, mutable, factoryMethod, isBasic);
				Application.currentApp.Log.println(Application.VERBOSE_STARTUP_LOG_LEVEL,"		Defined Classfactory Entry {0} type={1}",name, type);

				configInfo.Context["factoryItem"] = i.nickName;
			}

			public override void executeAfter(IConfigElement configInfo)
			{
				configInfo.Context.Remove("factoryItem");
			}
		}

		/// <summary>
		/// Handles Class definitions in a specific factory definition
		/// </summary>
		protected internal class FactoryItemPatternConfigHandler : ConfigHandler
		{
			protected static string[] s_legalAttributes = new string[]{};
			public override void executeBefore(IConfigElement configInfo)
			{
				IApplication app = Application.currentApp;
				configInfo.checkIllegalAttributes(s_legalAttributes);
				string pattern = configInfo.getChildText("").Trim();
				ClassFactory factory = (ClassFactory)configInfo.Context["factory"];
				Application.currentApp.Log.println(Application.VERBOSE_STARTUP_LOG_LEVEL,"				Registered Pattern \"{0}\"",pattern);
				DotNetClassInfo dnc = factory.classFromNickName(configInfo.Context["factoryItem"].ToString());
				factory.add(dnc.fullName, pattern, null, dnc.mutable, dnc.StaticFactoryMethod);
			}
		}

		/// <summary>
		/// Handles Class definitions in a specific factory definition
		/// </summary>
		protected internal class FactoryItemDefaultConfigHandler : ConfigHandler
		{
			protected static string[] s_legalAttributes = new string[]{};

			public override void executeBefore(IConfigElement configInfo)
			{
				IApplication app = Application.currentApp;
				configInfo.checkIllegalAttributes(s_legalAttributes);
				string defVal = configInfo.getChildText();
				ClassFactory factory = (ClassFactory)configInfo.Context["factory"];
				Application.currentApp.Log.println(Application.VERBOSE_STARTUP_LOG_LEVEL,"				Registered Default Value \"{0}\"",defVal);
				DotNetClassInfo dnc = factory.classFromNickName(configInfo.Context["factoryItem"].ToString());
				//factory.add(dnc.fullName, pattern, null, dnc.mutable, dnc.isParsable);
				dnc.defaultValue = defVal;
			}
		}

		/// <summary>
		/// Handles Class definitions in a specific factory definition
		/// </summary>
		protected internal class FactoryItemCustomPropConfigHandler : ConfigHandler
		{
			protected static string[] s_legalAttributes = new string[]{"name"};
			public override void executeBefore(IConfigElement configInfo)
			{
				IApplication app = Application.currentApp;
				configInfo.checkIllegalAttributes(s_legalAttributes);
				string name = configInfo.getAttribute("name");
				string defVal = configInfo.getChildText();
				if(name != null)
				{
					ClassFactory factory = (ClassFactory)configInfo.Context["factory"];
					Application.currentApp.Log.println(Application.VERBOSE_STARTUP_LOG_LEVEL,"				Registered Custom Property {0} with default value \"{1}\"",name,defVal);
					DotNetClassInfo dnc = factory.classFromNickName(configInfo.Context["factoryItem"].ToString());
					dnc.CustomProperties[name] = defVal;
				}
			}
		}

		protected internal class AppDataEntry
		{
			bool m_isEntry=false;
			private string m_name;
			private string m_type;
			private string m_nodeType;
			private object m_val;
			private string m_rawVal;
			private string m_parentNodeName;
			private string m_elementName;
			private INameAddressSupport m_container;
			public AppDataEntry(IConfigElement configInfo, INameAddressSupport container)
			{
				m_container = container;
				m_parentNodeName=configInfo.Element.ParentNode.Name;
				m_elementName=configInfo.Element.Name;
				if(configInfo.Element.Name == "Element")
				{
					m_isEntry = false;
					m_name = configInfo.getAttribute("name");
					m_nodeType = configInfo.getAttribute("type");
					m_rawVal = configInfo.getChildText();
					if(m_rawVal != null) 
						m_rawVal = m_rawVal.ToString().Trim();
				}
				else if(configInfo.Element.Name == "Entry")
				{
					m_isEntry = true;
					m_name = configInfo.getAttribute("name");
					m_type = configInfo.getAttribute("type");
					m_nodeType = configInfo.getAttribute("nodeType","");
					m_rawVal = configInfo.getChildText();
				}
			}
			public void register()
			{
				if (m_isEntry)
				{
					if (m_name.Contains("syrinxLocalizer"))
					{
						m_name = m_name;
					}
					if(m_type != null)
						m_val = Serialization.deserialize(m_type, m_rawVal,"ExternalTrusted");
					else
					{
						m_val = m_rawVal==null?null:m_rawVal.Trim();
					}
				}
				else
				{
					m_val = m_rawVal==null?null:m_rawVal.Trim();
				}
				if(m_name != null && m_name.Length > 0)
				{
					Application.currentApp.Log.println(Application.VERBOSE_STARTUP_LOG_LEVEL,"Added {4} {5} {0}, type={1}, value={2} stored in nodeType {3}", m_name, m_type, m_val != null ? m_val.ToString() : "null", m_nodeType,m_parentNodeName,m_elementName);
					m_container.put(m_name, m_val, m_nodeType.Length==0?null:m_nodeType);
				}
				else
				{
					Application.currentApp.Log.println(NORMAL_STARTUP_LOG_LEVEL, "Invalid map data for element.  You must specify both name and type");
					//TODO Throw some kind of exception
				}
				
			}

		}
		/// <summary>
		/// Handles Application or Session data definitions, adding them to the real 
		/// application and session objects.
		/// </summary>
		protected internal class AppSesDataElementConfigHandler : ConfigHandler
		{
			protected INameAddressSupport m_container;
			protected IList m_startupList;
			protected static string[] s_validAttributes = new string[]{"name","type","nodeType"};

			public AppSesDataElementConfigHandler(INameAddressSupport container, IList startupList)
			{
				m_container=container;
				m_startupList=startupList;
			}

			public override void executeBefore(IConfigElement configInfo)
			{
				IApplication app = Application.currentApp;
				configInfo.checkIllegalAttributes(s_validAttributes);
				AppDataEntry entry = new AppDataEntry(configInfo, m_container);
				try
				{
					entry.register();
				}
				catch(Exception)
				{
					m_startupList.Add(entry);
				}
				
/*				
				string name = configInfo.getAttribute("name");
				string type = configInfo.getAttribute("type");
				string val = configInfo.getChildText();
				if(name != null && type != null)
				{
					if(val != null)
						val = val.Trim();
					m_container.put(name, val, type);
				}
				else
				{
					Application.currentApp.Log.println("Invalid map data for element.  You must specify both name and type");
					//TODO Throw some kind of exception
				}
*/
			}
		}

		protected internal class LogObserversConfigHandler : ConfigHandler
		{

			protected static string[] s_validAttributes = new string[]{"type","logThresholdLevel","allowStartupLogging"};

			public override void executeBefore(IConfigElement configInfo)
			{
				IApplication app = Application.currentApp;
				IClassFactory m_callFactory = app.factories["CallTypes"];
				configInfo.checkIllegalAttributes(s_validAttributes);
				string type = configInfo.getAttribute("type");
				string logThresholdLevel="";
				string allowStartupLogging="";
				ISubject log = null;
				if(string.Compare(type, "log", true) ==0)
					log = app.Log;
				else if(string.Compare(type, "audit", true) == 0)
					log = app.Audit;
				else if(string.Compare(type, "trace", true) == 0)
					log = app.Trace;
				if(log != null)
				{
					XmlElement el = configInfo.Element;
					foreach(XmlNode n in el.ChildNodes)
					{
						if(n is XmlElement)
						{
							XmlElement child = (XmlElement)n;
							//log.addObserver((ICall)m_callFactory.createFromNickName(child.Name, "", child));
							logThresholdLevel = child.GetAttribute("logThresholdLevel");
							allowStartupLogging = child.GetAttribute("allowStartupLogging");
							if (string.Compare(type,"log",true) == 0)
								((Log)log).addObserver((ICall)m_callFactory.createFromNickName(child.Name, "", child), logThresholdLevel.Equals("")?-1:int.Parse(logThresholdLevel), allowStartupLogging.Equals("")?true:bool.Parse(allowStartupLogging));
							else
								log.addObserver((ICall)m_callFactory.createFromNickName(child.Name, "", child));
							Application.currentApp.Log.println(Application.VERBOSE_STARTUP_LOG_LEVEL,"Defined {0} observer {1} with definition {2}",type,child.Name,child.OuterXml );
						}
					}
				}
			}
		}
		protected internal class LoggingLevelsConfigHandler : ConfigHandler
		{
			protected static string[] s_validAttributes = new string[]{"startupLogging","logCaller","logThresholdLevel","traceThresholdLevel", "defaultLogLevel", "defaultTraceLevel"};

			public override void executeBefore(IConfigElement configInfo)
			{
				IApplication app = Application.currentApp;
				configInfo.checkIllegalAttributes(s_validAttributes);
				string logThresholdLevel = configInfo.getAttribute("logThresholdLevel", "0");
				string traceThresholdLevel = configInfo.getAttribute("traceThresholdLevel", "0");
				string defaultLogLevel = configInfo.getAttribute("defaultLogLevel", "0");
				string defaultTraceLevel = configInfo.getAttribute("defaultTraceLevel", "0");
				string startupLogLevel = configInfo.getAttribute("startupLogging","normal");
				string logCaller = configInfo.getAttribute("logCaller","false");
				if (startupLogLevel.ToLower() == "verbose")
					Application.s_mainApp.m_startupLoggingLevel = VERBOSE_STARTUP_LOG_LEVEL;
				else if (startupLogLevel.ToLower() == "none")
					Application.s_mainApp.m_startupLoggingLevel = -1;
				else
					Application.s_mainApp.m_startupLoggingLevel = NORMAL_STARTUP_LOG_LEVEL;
				Application.currentApp.Log.println(Application.VERBOSE_STARTUP_LOG_LEVEL,"Defined Logging Options: logThresholdLevel={0} traceThresholdLevel=(1) defaultLogLevel=(2) defaultTraceLevel=(3) startupLogging=(4) logCaller={5}",logThresholdLevel,traceThresholdLevel,defaultLogLevel,defaultTraceLevel,startupLogLevel, logCaller);
				Log l = (Log)app.Log;
				l.DefaultLevel = Int32.Parse(defaultLogLevel);
				//We need to temporarily store the configured threshold,
				//and set the threshold to the configured startup level so that 
				//we can accurately log startup entries.  The threshold will be 
				//set back to the configured value after the application is loaded.
				Application.s_mainApp.m_loggingThreshold = Int32.Parse(logThresholdLevel);
				l.NotifyThreshold = Application.s_mainApp.m_startupLoggingLevel;
				foreach(LogCall call in l.Observers)
				{
					call.startupLoggingLevel = Application.s_mainApp.m_startupLoggingLevel;
				}
				l.LogCaller = bool.Parse(logCaller);
				Swaf.Logging.Trace t = (Swaf.Logging.Trace)app.Trace;
				t.DefaultLevel = Int32.Parse(defaultTraceLevel);
				t.NotifyThreshold = Int32.Parse(traceThresholdLevel);				
			}
		}
		
		protected internal class ExceptionLoggingConfigHandler : ConfigHandler
		{
			protected static string[] s_validAttributes = new string[]{"traceExceptions", "traceLevel", "logExceptions", "logLevel"};

			public override void executeBefore(IConfigElement configInfo)
			{
				IApplication app = Application.currentApp;
				configInfo.checkIllegalAttributes(s_validAttributes);
				string traceExceptions = configInfo.getAttribute("traceExceptions", "false");
                string traceLevel = configInfo.getAttribute("traceLevel", "0");
                string logExceptions = configInfo.getAttribute("logExceptions", "false");
                string logLevel = configInfo.getAttribute("logLevel", "0");
				Application.currentApp.Log.println(Application.VERBOSE_STARTUP_LOG_LEVEL,"Defined Exception Logging Options: traceExceptions={0}, traceLevel={1}, logLevel={2}",traceExceptions,traceLevel,logLevel);
				if(String.Compare("true", traceExceptions, true) == 0)
					BaseException.defineTrace(app.Trace);
                if (String.Compare("true", logExceptions, true) == 0)
                    BaseException.LogExceptions = true;

				BaseException.ExceptionTraceLevel = Int32.Parse(traceLevel);
				BaseException.DefaultLogLevel = Int32.Parse(logLevel);
			}
		}

		protected internal class TimingConfigHandler : ConfigHandler
		{
			protected static DateTime s_lastTime = DateTime.Now;
			public override void executeBefore(IConfigElement configInfo)
			{
				DateTime now = DateTime.Now;
				long ticks = now.Ticks - s_lastTime.Ticks;
#if(!COMPACT_FRAMEWORK)				
				double ms = ((double)ticks/1000000);
#else
				double ms = ((double)ticks/10000);
#endif
				Application.currentApp.Log.println(NORMAL_STARTUP_LOG_LEVEL, String.Format("{0}:{1}({2}ms)", configInfo.getChildText(), now.TimeOfDay, ms.ToString()));
				s_lastTime = now;
			}
		}

		protected internal class RemotingSupportConfigHandler : ConfigHandler
		{
			protected static string[] s_legalAttributes = new string[] { "httpPort", "tcpPort", "name" };

			public override void executeBefore(IConfigElement configInfo)
			{
				//IApplication app = (IApplication)configInfo.Context["app"];
				//configInfo.checkIllegalAttributes(s_legalAttributes);
				IList otherOptions = configInfo.checkUnknownAttributes(s_legalAttributes);
				string httpPort = configInfo.getAttribute("httpPort", null);
				string tcpPort = configInfo.getAttribute("tcpPort", null);
				string name = configInfo.getAttribute("name", "");

				if (name != "" && (httpPort != null || tcpPort != null))
				{
					if (tcpPort != null && !tcpPort.Equals(""))
						Application.currentApp.Log.println(Application.NORMAL_STARTUP_LOG_LEVEL, "Rule Manager {0} is exposed via Remoting via TCP on Port {1}", name, tcpPort);
					if (httpPort != null && !httpPort.Equals(""))
						Application.currentApp.Log.println(Application.NORMAL_STARTUP_LOG_LEVEL, "Rule Manager {0} is exposed via Remoting via HTTP on Port {1}", name, httpPort);
					ApplicationRemoteServer rms = new ApplicationRemoteServer(name, httpPort, tcpPort, otherOptions);
					configInfo.Context["rms"] = rms;
				}
			}

			public override void executeAfter(IConfigElement configInfo)
			{
				configInfo.Context.Remove("rms");
			}

		}

		protected internal class FeatureDependancyConfigHandler : ConfigHandler
		{
			protected static string[] s_legalAttributes = new string[] { "type", "stripDepth" };

			public override void executeBefore(IConfigElement configInfo)
			{
				//IApplication app = (IApplication)configInfo.Context["app"];
				//configInfo.checkIllegalAttributes(s_legalAttributes);
				string depType = configInfo.getAttribute("type", "");
				string stripDepth = configInfo.getAttribute("stripDepth", "0");

				if (depType.CompareTo("binary") == 0)
				{
					int depth = 0;
					int.TryParse(stripDepth, out depth);
					string assemblyName = configInfo.getChildText();
					if (!Cmn.IsEmpty(assemblyName))
						AssRez.Instance.addAssembly(assemblyName, depth);
				}
			}

			public override void executeAfter(IConfigElement configInfo)
			{
				configInfo.Context.Remove("rms");
			}

		}

		protected internal class CallDelegateWrapper
		{
			ICall m_handler=null;
			public CallDelegateWrapper(ICall handler)
			{
				m_handler=handler;
			}
			public void makeCall(IApplication app)
			{
				m_handler.call(app);
			}
		}
		protected internal class SessionValidatorConfigHandler : ConfigHandler
		{
			protected static string[] s_validAttributes = new string[]{};
			protected Application m_app;
			public SessionValidatorConfigHandler(Application app)
			{
				m_app = app;
			}

			public override void executeBefore(IConfigElement configInfo)
			{
				IClassFactory callFactory = m_app.factories["CallTypes"];
				configInfo.checkIllegalAttributes(s_validAttributes);
				XmlElement ele = configInfo.Element;
				foreach(XmlNode child in ele.ChildNodes)
				{
					if (child.NodeType == XmlNodeType.Element)
					{
						m_app.SessionValidator = (ICall)callFactory.createFromNickName(child.Name, "", child);
						break;
					}
				}
			}
		}

		protected internal class ApplicationEventsConfigHandler : ConfigHandler
		{
			protected static string[] s_validAttributes = new string[]{};
			protected IApplication m_app;

			public ApplicationEventsConfigHandler(IApplication app)
			{
				m_app = app;
			}

			public override void executeBefore(IConfigElement configInfo)
			{
				IClassFactory callFactory = m_app.factories["CallTypes"];
				IAppManagement appMgmt = (IAppManagement) m_app;
				configInfo.checkIllegalAttributes(s_validAttributes);
				XmlElement ele = configInfo.Element;
				foreach(XmlNode child in ele.ChildNodes)
				{
					CallDelegateWrapper handler=null;
					if (child.HasChildNodes)
					{
						foreach(XmlNode grandChild in child.ChildNodes)
						{
							if (grandChild.NodeType == XmlNodeType.Element)
							{
								handler = new CallDelegateWrapper((ICall)callFactory.createFromNickName(grandChild.Name, "", grandChild));
								if (child.Name.CompareTo("ApplicationStart")==0)
								{
									appMgmt.OnAppStart += new AppStartRequest(handler.makeCall);
								}
								else if(child.Name.CompareTo("ApplicationStop") ==0)
								{
									appMgmt.OnAppShutdown += new AppShutdownRequest(handler.makeCall);
								}
								else if(child.Name.CompareTo("SessionStart")==0)
								{
									appMgmt.OnSessionStart += new SessionStartRequest(handler.makeCall);
								}
								else if(child.Name.CompareTo("SessionStop")==0)
								{
									appMgmt.OnSessionShutdown += new SessionShutdownRequest(handler.makeCall);
								}
							}
						}
					}
				}
			}
		}
		/// <summary>
		/// For the Global Application this should always return True.
		/// </summary>
		public bool isValidSession
		{
			get
			{
				return true;;
			}
		}
		public ISessionInfo sessionInfo
		{
			get
			{
				return new SessionInfo(isValidSession, this.sessionId, securityMgr.currentPrincipal.userName,securityMgr.currentPrincipal.instanceId, securityMgr.currentPrincipal.additionalInfo);
			}
		}
		public bool isTiedToRemoteGlobalApp
		{
			get
			{
				return true;
			}
			set
			{
				//Do nothing.
			}
		}
		internal ICall SessionValidator
		{
			get
			{
				return m_sessionValidator;
			}
			set
			{
				m_sessionValidator=value;
			}
		}
	}


public class FactoryManager : MarshalByRefObject, IFactoryManager
	{
		protected IDictionary m_factories;

		public FactoryManager(IDictionary factories)
		{
			m_factories = factories;
		}

		public IClassFactory this[string factoryName]
		{
			get
			{
				return (IClassFactory)m_factories[factoryName];
			}
		}
	}

}
